package com.cloudlead.common.worker.interceptor;

import java.text.SimpleDateFormat;
import java.util.*;

import com.cloudlead.common.lang.Messages;
import com.cloudlead.common.lang.api.ResponseCode;
import com.cloudlead.common.lang.persistence.OnlineUser;
import com.cloudlead.common.lang.security.Principal;
import com.cloudlead.common.worker.desc.security.RequireGroupsDesc;
import com.cloudlead.common.lang.api.ActionResponse;
import com.cloudlead.common.lang.api.SimpleActionResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import com.cloudlead.common.worker.ActionContext;
import com.cloudlead.common.worker.ApiRegistry;
import com.cloudlead.common.worker.Interceptor;
import com.cloudlead.common.worker.InterceptorChain;
import com.cloudlead.common.worker.annotation.Order;
import com.cloudlead.common.worker.annotation.Provider;
import com.cloudlead.common.worker.desc.ActionDesc;
import com.cloudlead.common.worker.desc.security.RequirePrivilegesDesc;
import com.cloudlead.common.worker.desc.security.RequireRolesDesc;
import com.cloudlead.common.worker.desc.security.SecurityDesc;
import com.cloudlead.common.lang.security.Subject;
import com.cloudlead.common.lang.security.SecurityThreadContext;
import com.cloudlead.common.worker.security.TokenInfoService;
import com.cloudlead.common.worker.security.annotation.Logical;

/**
 * 权限拦截器
 *
 * @author dean.lu
 */
@Provider
@Order(2)
public class SecurityCheckInterceptor implements Interceptor {

    @Value("${security.hasLoginCheck}")
    private boolean hasLoginCheck;

    @Value("${security.hasAuth}")
    private boolean hasAuth;

    @Autowired(required = false)
    private TokenInfoService tokenInfoService;
    @Autowired
    private ApiRegistry apiRegistry;

        @Override
    public void intercept(ActionContext actionConfig, InterceptorChain chain) throws Exception {
        ActionDesc actionDesc = apiRegistry.getApiDesc(actionConfig.getActionName());
        SecurityDesc securityDesc = actionDesc.getSecurityDesc();
        Subject subject = null;
        if (StringUtils.isNotBlank(actionConfig.getRequest().getAccessToken())) {
            subject = tokenInfoService.getSubjectByAccessToken(actionConfig.getRequest().getAccessToken());
            if(subject != null){
                OnlineUser.updateTime(subject.getPrincipal().getUsername());
            }
            SecurityThreadContext.setSubject(subject);
        }
        if (!securityDesc.isRequireGuest() && hasLoginCheck) {
            if (StringUtils.isBlank(actionConfig.getRequest().getAccessToken())) {
                SimpleActionResponse response = new SimpleActionResponse(false, ActionResponse.Level.warn, Messages.getMessage("worker.action.security.token.no"), ResponseCode.SC_UNAUTHORIZED.code(), null);
                actionConfig.setResponse(response);
                return;
            }
            if (null == subject) {
                SimpleActionResponse response = new SimpleActionResponse(false, ActionResponse.Level.warn, Messages.getMessage("worker.action.security.token.expire"), ResponseCode.SC_UNAUTHORIZED.code(), null);
                actionConfig.setResponse(response);
                return;
            } else if (hasAuth) {
                if (checkAuth(actionDesc, subject)) {
                    chain.process(actionConfig);
                } else {
                    SimpleActionResponse response = new SimpleActionResponse(false, ActionResponse.Level.warn, Messages.getMessage("worker.action.security.auth.no"), ResponseCode.SC_UNAUTHORIZED.code(), null);
                    actionConfig.setResponse(response);
                    return;
                }
            } else {
                chain.process(actionConfig);
            }
        } else {
            chain.process(actionConfig);
        }
    }

    private boolean checkAuth(ActionDesc actionDesc, Subject subject) {
        SecurityDesc securityDesc = actionDesc.getSecurityDesc();
        if (securityDesc.isRequireGuest()) {
            return true;
        }
        List<RequirePrivilegesDesc> requirePrivilegesDescs = securityDesc.getRequirePrivilegesDescs();
        List<RequireRolesDesc> requireRolesDescs = securityDesc.getRequireRolesDescs();
        List<RequireGroupsDesc> requireGroupsDescs = securityDesc.getRequireGroupsDescs();
        if (null == requirePrivilegesDescs && null == requireRolesDescs && null == requireGroupsDescs) {
            return true;
        } else {
            if (null != requirePrivilegesDescs) {
                List<String> privileges = subject.getPrivileges();
                if (null == privileges || privileges.isEmpty()) {
                    return false;
                }
                for (RequirePrivilegesDesc requirePrivilegesDesc : requirePrivilegesDescs) {
                    Logical logical = requirePrivilegesDesc.logical();
                    String[] value = requirePrivilegesDesc.value();
                    if (Logical.AND.equals(logical)) {
                        for (String privilege : value) {
                            if (!privileges.contains(privilege)) {
                                return false;
                            }
                        }
                    } else {
                        boolean pass = false;
                        for (String privilege : value) {
                            if (privileges.contains(privilege)) {
                                pass = true;
                                break;
                            }
                        }

                        if (!pass) {
                            return false;
                        }
                    }
                }
            }
            if (null != requireRolesDescs) {
                List<String> roles = subject.getRoles();
                if (null == roles || roles.isEmpty()) {
                    return false;
                }
                for (RequireRolesDesc requireRolesDesc : requireRolesDescs) {
                    Logical logical = requireRolesDesc.logical();
                    String[] value = requireRolesDesc.value();
                    if (Logical.AND.equals(logical)) {
                        for (String role : value) {
                            if (!roles.contains(role)) {
                                return false;
                            }
                        }
                    } else {
                        boolean pass = false;
                        for (String role : value) {
                            if (roles.contains(role)) {
                                pass = true;
                                break;
                            }
                        }
                        if (!pass) {
                            return false;
                        }
                    }
                }
            }
            if (null != requireGroupsDescs) {
                List<String> groups = subject.getGroups();
                if (null == groups || groups.isEmpty()) {
                    return false;
                }
                for (RequireGroupsDesc requireGroupsDesc : requireGroupsDescs) {
                    Logical logical = requireGroupsDesc.logical();
                    String[] value = requireGroupsDesc.value();
                    if (Logical.AND.equals(logical)) {
                        for (String group : value) {
                            if (!groups.contains(group)) {
                                return false;
                            }
                        }
                    } else {
                        boolean pass = false;
                        for (String group : value) {
                            if (groups.contains(group)) {
                                pass = true;
                                break;
                            }
                        }
                        if (!pass) {
                            return false;
                        }
                    }
                }
            }
            return true;
        }
    }
}
